package com.hk.core.web.reactive;

import com.hk.commons.util.Constants;
import com.hk.commons.util.StringUtils;
import lombok.SneakyThrows;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.filter.reactive.ServerWebExchangeContextFilter;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.util.context.Context;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import static com.hk.commons.util.Constants.*;
import static org.springframework.http.HttpHeaders.CONTENT_DISPOSITION;
import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM;

/**
 * web相关的工具类
 *
 * @author kevin
 * @date 20121年7月07日 下午2:51:02
 */
public abstract class Webs {

    /**
     * 获取 user-Agent 信息
     *
     * @param request request
     * @return User-Agent
     */
    public static String getUserAgent(ServerHttpRequest request) {
        return request.getHeaders().getFirst(HttpHeaders.USER_AGENT);
    }

    public static Mono<ServerWebExchange> getServerWebExchange() {
        return Mono.deferContextual(contextView -> Mono.justOrEmpty(ServerWebExchangeContextFilter.getExchange(Context.of(contextView))));
    }

    /**
     * 获取所有请求参数
     *
     * @param request request
     * @return 请求参数
     */
    public static Map<String, String> getRequestParam(ServerHttpRequest request) {
        var queryParams = request.getQueryParams();
        var result = new HashMap<String, String>();
        for (var entry : queryParams.entrySet()) {
            var value = String.join(StringUtils.COMMA_SEPARATE, entry.getValue());
            if (StringUtils.isNotEmpty(value)) {
                result.put(entry.getKey(), value);
            }
        }
        return result;
    }

    /**
     * 文件下载或预览
     *
     * @param fileName fileName
     * @param body     body
     * @return ResponseEntity
     */
    public static Mono<ResponseEntity<Resource>> toResponseEntity(String fileName, byte[] body) {
        return toResponseEntity(fileName, getMediaType(fileName), body.length, new ByteArrayResource(body));
    }

    /**
     * 文件下载或预览
     *
     * @param fileName fileName
     * @param resource resource
     * @return ResponseEntity
     */
    public static Mono<ResponseEntity<Resource>> toResponseEntity(String fileName, MediaType mediaType, long contentLength, Resource resource) {
        return getServerWebExchange().map(exchange -> {
            var request = exchange.getRequest();
            var builder = ResponseEntity.ok();
            if (StringUtils.isNotEmpty(fileName)) {
                String attachFileName = obtainAttachFileName(fileName, getUserAgent(request));
                builder.header(CONTENT_DISPOSITION, STR."attachment;filename=\{attachFileName}");
            }
            return builder.contentLength(contentLength)
                    .contentType(mediaType)
                    .body(resource);
        }).switchIfEmpty(Mono.error(new NullPointerException(STR."\{StringUtils.defaultIfEmpty(fileName, resource.getFilename())} not found")));
    }

    @SneakyThrows
    public static Mono<ResponseEntity<Resource>> toResponseEntity(MediaType mediaType, Resource resource) {
        return toResponseEntity(resource.getFilename(), mediaType, resource.contentLength(), resource);
    }

    /**
     * 预览图片
     */
    @SneakyThrows
    public static Mono<ResponseEntity<Resource>> toPreviewResponseEntity(MediaType mediaType, Resource resource) {
        return toResponseEntity(null, mediaType, resource.contentLength(), resource);
    }

    public static MediaType getMediaType(String fileName) {
        return MediaTypeFactory.getMediaType(fileName).orElse(APPLICATION_OCTET_STREAM);
    }

    /**
     * 附件名
     *
     * @param fileName fileName
     * @return fileName
     */
    @SneakyThrows
    private static String obtainAttachFileName(String fileName, String agent) {
        var encodeFileName = fileName;
        if (Objects.nonNull(agent)) {
            if (agent.contains(EDGE_USER_AGENT_HEADER_VALUE) || agent.contains(MSIE_USER_AGENT_HEADER_VALUE)
                    || agent.contains(TRIDENT_USER_AGENT_HEADER_VALUE)) {// IE
                encodeFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
            } else if (agent.contains(MOZILLA_USER_AGENT_HEADER_VALUE)) {// 火狐,谷歌
                encodeFileName = StringUtils.newStringIso8859_1(StringUtils.getByteUtf8(fileName));
            }
        }
        return encodeFileName;
    }


    /**
     * 获取请求地址
     *
     * @param request request
     * @return ip address
     */
    public static String getRemoteAddr(ServerHttpRequest request) {
        var headers = request.getHeaders();
        var ip = headers.getFirst(X_FORWARDED_FOR_HEADER);
        String[] ipHeaderNames = {"Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"};
        var index = 0;
        while (index < ipHeaderNames.length &&
                (StringUtils.isEmpty(ip) || StringUtils.equalsIgnoreCase(Constants.UNKNOWN, ip))) {
            ip = headers.getFirst(ipHeaderNames[index]);
            index++;
        }
        if (StringUtils.isEmpty(ip) || StringUtils.equalsIgnoreCase(Constants.UNKNOWN, ip)) {
            var remoteAddress = request.getRemoteAddress();
            if (remoteAddress != null) {
                ip = remoteAddress.getHostString();
            }
        }
        var ipArray = StringUtils.splitByComma(ip);
        return Arrays.stream(ipArray)
                .filter(item -> !StringUtils.equalsIgnoreCase(item, Constants.UNKNOWN))
                .findFirst().orElse(null);
    }
}
